#include <cstdint>
#include <cstdio>
#include <random>
#include <cmath>
#include <systemc>
#include <queue>

using namespace sc_core;
using namespace sc_dt;

constexpr uint32_t Log2(uint32_t n) {
    return n <= 1 ? 0 : 1 + Log2(n >> 1);
}

template<uint32_t DATA_WIDTH = 8, uint32_t FIFO_DEPTH = 4, uint32_t ADDR_WIDTH = Log2(FIFO_DEPTH)>
SC_MODULE(SyncFIFO) {
    sc_in<bool>                 clock;
    sc_in<bool>                 reset_n;
    sc_in<sc_uint<DATA_WIDTH>>  wdata;
    sc_out<sc_uint<DATA_WIDTH>> rdata;
    sc_in<bool>                 wreq;
    sc_in<bool>                 rreq;
    sc_out<bool>                full;
    sc_out<bool>                empty;

    sc_signal<sc_uint<DATA_WIDTH>>      buffer[FIFO_DEPTH];
    sc_signal<sc_uint<ADDR_WIDTH>>      wptr, rptr;
    sc_signal<sc_uint<ADDR_WIDTH+1>>    count;

    void comb_logic() {
        full.write(count.read() == FIFO_DEPTH);
        empty.write(count.read() == 0);
    }

    void write_logic() {
        if (!reset_n) {
            wptr.write(0);
        }
        else if (wreq.read() && !full.read()) {
            auto wptr_val = wptr.read();
            buffer[wptr_val].write(wdata.read());
            wptr.write((wptr_val + 1) % FIFO_DEPTH);
        }
    }

    void read_logic() {
        if (!reset_n) {
            rptr.write(0);
        }
        else if (rreq.read() && !empty.read()) {
            auto rptr_val = rptr.read();
            rdata.write(buffer[rptr_val].read());
            rptr.write((rptr_val + 1) % FIFO_DEPTH);
        }
    }

    void count_logic() {
        auto wen = wreq.read() && !full.read();
        auto ren = rreq.read() && !empty.read();
        if (!reset_n) {
            count.write(0);
        }
        else {
            if (wen && ren) {
                // count.write(count.read()); // No change
            }
            else if (wen) {
                count.write(count.read() + 1);
            }
            else if (ren) {
                count.write(count.read() - 1);
            }
        }
    }

    SC_CTOR(SyncFIFO) {
        SC_METHOD(comb_logic);
        sensitive << count;

        SC_METHOD(write_logic);
        sensitive << clock.pos() << reset_n.neg();
        dont_initialize();

        SC_METHOD(read_logic);
        sensitive << clock.pos() << reset_n.neg();
        dont_initialize();

        SC_METHOD(count_logic);
        sensitive << clock.pos() << reset_n.neg();
        dont_initialize();
    }
};

int sc_main(int argc, char **argv) {
    const int DATA_WIDTH = 8; // Data width
    const int FIFO_DEPTH = 4; // FIFO depth

    // 声明信号
    const int period = 10; // 时钟周期为10ns
    sc_clock clock("clock", period, SC_NS);
    sc_signal<bool> reset_n;
    sc_signal<sc_uint<DATA_WIDTH>> wdata, rdata;
    sc_signal<bool> wreq, rreq;
    sc_signal<bool> full, empty;
    
    // 实例化SyncFIFO模块
    SyncFIFO<8, 4> fifo("fifo");

    // 绑定端口
    fifo.clock(clock);
    fifo.reset_n(reset_n);
    fifo.wdata(wdata);
    fifo.rdata(rdata);
    fifo.wreq(wreq);
    fifo.rreq(rreq);
    fifo.full(full);
    fifo.empty(empty);

    // 记录波形
    sc_trace_file *tf = sc_create_vcd_trace_file("sync_fifo");
    tf->set_time_unit(1, SC_NS);
    sc_trace(tf, clock, "clock");
    sc_trace(tf, reset_n, "reset_n");
    sc_trace(tf, wdata, "wdata");
    sc_trace(tf, rdata, "rdata");
    sc_trace(tf, wreq, "wreq");
    sc_trace(tf, rreq, "rreq");
    sc_trace(tf, full, "full");
    sc_trace(tf, empty, "empty");
    sc_trace(tf, fifo.wptr, "wptr");
    sc_trace(tf, fifo.rptr, "rptr");
    sc_trace(tf, fifo.count, "count");

    // 随机数生成器
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> data_dis(0, 0xFF);
    std::uniform_int_distribution<> rwop_dis(0, 1);
    
    // 数据队列
    std::queue<unsigned int> data_queue;

    // 复位FIFO
    printf("@%s: Applying reset...\n", sc_time_stamp().to_string().c_str());
    reset_n.write(false);
    wreq.write(false);
    rreq.write(false);
    wdata.write(0);
    sc_start(2 * period, SC_NS); 
    reset_n.write(true);
    printf("@%s: Reset released.\n", sc_time_stamp().to_string().c_str());
    sc_start(2 * period, SC_NS);

    // 写满FIFO
    printf("\n--- Test Case 1: Writing until FIFO is full ---\n");
    for (int i = 0; i < FIFO_DEPTH; ++i) {
        unsigned int data = data_dis(gen);
        wdata.write(data);
        wreq.write(true);
        data_queue.push(data);
        printf("@%s: Writing data: 0x%X\n", sc_time_stamp().to_string().c_str(), data);
        sc_start(period, SC_NS);
    }
    wreq.write(false);
    sc_start(period, SC_NS);
    printf("@%s: FIFO should be full. Full signal: %d\n", sc_time_stamp().to_string().c_str(), full.read());

    // 满时写入
    printf("\n--- Test Case 2: Attempting to write while full ---\n");
    wdata.write(0xAA);
    wreq.write(true);
    printf("@%s: Trying to write 0xAA. Write should be ignored.\n", sc_time_stamp().to_string().c_str());
    sc_start(period, SC_NS);
    wreq.write(false);
    sc_start(period, SC_NS);

    // 读空FIFO
    printf("\n--- Test Case 3: Reading until FIFO is empty ---\n");
    for (int i = 0; i < FIFO_DEPTH; ++i) {
        rreq.write(true);
        sc_start(period, SC_NS);

        unsigned int expected_data = data_queue.front();
        data_queue.pop();
        unsigned int read_data = rdata.read();
        printf("@%s: Read data: 0x%X (Expected: 0x%X)\n", sc_time_stamp().to_string().c_str(), read_data, expected_data);
        if (read_data != expected_data) {
            SC_REPORT_ERROR("TESTBENCH", "Data mismatch!");
        }
    }
    rreq.write(false);
    sc_start(period, SC_NS);
    printf("@%s: FIFO should be empty. Empty signal: %d\n", sc_time_stamp().to_string().c_str(), empty.read());

    // 随机读写
    printf("\n--- Test Case 4: Random read/write ---\n");

    for (int i = 0; i < 1500; ++i) {
        bool req_read = rwop_dis(gen);
        bool req_write = rwop_dis(gen);
        bool do_write = req_write && !full.read();
        bool do_read = req_read && !empty.read();

        printf("@%s: ", sc_time_stamp().to_string().c_str());

        // 写操作
        wreq.write(req_write);
        if (do_write) {
            unsigned int data = data_dis(gen);
            wdata.write(data);
            data_queue.push(data);
            printf("[W]");
        } else {
            printf("[ ]");
        }
        
        // 读操作
        rreq.write(req_read);
        if (do_read) {
            printf("[R]");
        } else {
            printf("[ ]");
        }
        
        // 执行
        sc_start(period, SC_NS);
        
        // 检查
        if (do_read) {
            unsigned int expected_data = data_queue.front();
            data_queue.pop();
            unsigned int read_data = rdata.read();
            printf(" |-> Got: 0x%02X", read_data);
            if (read_data != expected_data) {
                printf(" (ERROR: Expected 0x%02X)", expected_data);
                SC_REPORT_ERROR("TESTBENCH", "Data mismatch in random test!");
            }
        }
        printf("\n");
    }
    
    // 停止所有操作
    wreq.write(false);
    rreq.write(false);

    sc_start(2 * period, SC_NS);
    printf("\n--- Test Finished ---\n");
    sc_close_vcd_trace_file(tf);

    return 0;
}

